<!DOCTYPE html>
<html lang="zh">
<head><meta name="generator" content="Hexo 3.9.0">
    <meta charset="utf-8">
<title>MyBatis两级缓存机制详解 - 听到微笑的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">



    <meta name="description" content="​    缓存是提高软硬件系统性能的一种重要手段；硬件层面，现代先进CPU有三级缓存，而MyBatis也提供了缓存机制，通过缓存机制可以大大提高我们查询性能。">
<meta name="keywords" content="MyBatis,缓存">
<meta property="og:type" content="article">
<meta property="og:title" content="MyBatis两级缓存机制详解">
<meta property="og:url" content="http://www.tianjindong.cn/article/51583.html">
<meta property="og:site_name" content="听到微笑的博客">
<meta property="og:description" content="​    缓存是提高软硬件系统性能的一种重要手段；硬件层面，现代先进CPU有三级缓存，而MyBatis也提供了缓存机制，通过缓存机制可以大大提高我们查询性能。">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="http://www.tianjindong.cn/gallery/thumbnails/f6.jpg">
<meta property="og:updated_time" content="2020-08-22T06:32:33.666Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="MyBatis两级缓存机制详解">
<meta name="twitter:description" content="​    缓存是提高软硬件系统性能的一种重要手段；硬件层面，现代先进CPU有三级缓存，而MyBatis也提供了缓存机制，通过缓存机制可以大大提高我们查询性能。">
<meta name="twitter:image" content="http://www.tianjindong.cn/gallery/thumbnails/f6.jpg">







<link rel="icon" href="/images/favicon.ico">


<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.7.2/css/bulma.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Ubuntu:400,600|Source+Code+Pro">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/atom-one-dark.css">


    
    
    
    <style>body>.footer,body>.navbar,body>.section{opacity:0}</style>
    

    
    
    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lightgallery@1.6.8/dist/css/lightgallery.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/css/justifiedGallery.min.css">
    

    
    

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/outdatedbrowser@1.1.5/outdatedbrowser/outdatedbrowser.min.css">


    
    
    
    

<link rel="stylesheet" href="/css/back-to-top.css">


    
    

    
    
<script>
var _hmt = _hmt || [];
(function() {
    var hm = document.createElement("script");
    hm.src = "//hm.baidu.com/hm.js?81f9c1489f84c7a99b0bd377c9fb5de8";
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(hm, s);
})();
</script>

    
    

    
    
<link rel="stylesheet" href="/css/progressbar.css">
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

    
    
    

    
    
    


<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/iconfont.css">
</head>
<body class="is-2-column">
    <nav class="navbar navbar-main">
    <div class="container">
        <div class="navbar-brand is-flex-center">
            <a class="navbar-item navbar-logo" href="/">
            
                <img src="/images/logo.png" alt="MyBatis两级缓存机制详解" height="28">
            
            </a>
        </div>
        <div class="navbar-menu">
            
            <div class="navbar-start">
                
                <a class="navbar-item" href="/">主页</a>
                
                <a class="navbar-item" href="/categories">分类</a>
                
                <a class="navbar-item" href="/tags">标签</a>
                
                <a class="navbar-item" href="/archives">历史</a>
                
                <a class="navbar-item" href="/music.html">音乐</a>
                
                <a class="navbar-item" href="/about.html">关于</a>
                
            </div>
            
            <div class="navbar-end">
                
                
                <a class="navbar-item is-hidden-tablet catalogue" title="目录" href="javascript:;">
                    <i class="fas fa-list-ul"></i>
                </a>
                
                
                <a class="navbar-item search" title="搜索" href="javascript:;">
                    <i class="fas fa-search"></i>
                </a>
                
            </div>
        </div>
    </div>
</nav>
        
    <section class="section">
        <div class="container">
            <div class="columns">
                <div class="column is-8-tablet is-9-desktop is-9-widescreen has-order-2 column-main"><div class="card">
    
    <div class="card-image">
        <span class="image is-7by1">
            <img class="thumbnail" src="/gallery/thumbnails/f6.jpg" alt="MyBatis两级缓存机制详解">
        </span>
    </div>
    
    <div class="card-content article ">
        
        <div class="level article-meta is-size-7 is-uppercase is-mobile is-overflow-x-auto">
            <div class="level-left">
                <time class="level-item has-text-grey" datetime="2019-08-05T04:35:53.000Z">2019-08-05</time>
                
                <div class="level-item">
                <a class="has-link-grey -link" href="/categories/常用框架/">常用框架</a>&nbsp;/&nbsp;<a class="has-link-grey -link" href="/categories/常用框架/MyBatis/">MyBatis</a>
                </div>
                
                
                <span class="level-item has-text-grey">
                    
                    
                    18 分钟 读完 (大约 2716 个字)
                </span>
                
                
            </div>
        </div>
        
        <h1 class="title is-size-3 is-size-4-mobile has-text-weight-normal">
            
                MyBatis两级缓存机制详解
            
        </h1>
        <div class="content">
            <p>​    缓存是提高软硬件系统性能的一种重要手段；硬件层面，现代先进CPU有三级缓存，而MyBatis也提供了缓存机制，通过缓存机制可以大大提高我们查询性能。<a id="more"></a></p>
<h2 id="一级缓存"><a href="#一级缓存" class="headerlink" title="一级缓存"></a><strong>一级缓存</strong></h2><p>​    Mybatis对缓存提供支持，但是在没有配置的默认情况下，它只开启一级缓存，一级缓存只是相对于同一个SqlSession而言，一级缓存又叫本地缓存。所以在参数和SQL完全一样的情况下，我们使用同一个SqlSession对象调用一个Mapper方法，往往只执行一次SQL，因为使用SelSession第一次查询后，MyBatis会将其放在缓存中，以后再查询的时候，如果没有声明需要刷新，并且缓存没有超时的情况下，SqlSession都会取出当前缓存的数据，而不会再次发送SQL到数据库。</p>


<p>为什么要使用一级缓存，不用多说也知道个大概。但是还有几个问题我们要注意一下。</p>
<h3 id="一级缓存的生命周期"><a href="#一级缓存的生命周期" class="headerlink" title="一级缓存的生命周期"></a><strong>一级缓存的生命周期</strong></h3><ol>
<li>MyBatis在开启一个数据库会话时，会创建一个新的SqlSession对象，SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象；当会话结束时，SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。</li>
<li>如果SqlSession调用了<code>close()</code>方法，会释放掉一级缓存PerpetualCache对象，一级缓存将不可用。</li>
<li>如果SqlSession调用了<code>clearCache()</code>，会清空PerpetualCache对象中的数据，但是该对象仍可使用。- </li>
<li>SqlSession中执行了任何一个update操作(update()、delete()、insert()) ，都会清空PerpetualCache对象的数据，但是该对象可以继续使用。</li>
</ol>
<h3 id="如何判断两次查询是完全相同的呢"><a href="#如何判断两次查询是完全相同的呢" class="headerlink" title="如何判断两次查询是完全相同的呢"></a><strong>如何判断两次查询是完全相同的呢</strong></h3><p>mybatis认为，对于两次查询，如果以下条件都完全一样，那么就认为它们是完全相同的两次查询。</p>
<ul>
<li>传入的statementId</li>
<li>查询时要求的结果集中的结果范围</li>
<li>这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串（boundSql.getSql() ）</li>
<li>传递给java.sql.Statement要设置的参数值</li>
</ul>
<h3 id="一级缓存的测试"><a href="#一级缓存的测试" class="headerlink" title="一级缓存的测试"></a><strong>一级缓存的测试</strong></h3><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FirstCachedTest</span> </span>&#123;</span><br><span class="line">    <span class="hljs-meta">@Test</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        InputStream resource = Resources.getResourceAsStream(<span class="hljs-string">"SqlMapConfig.xml"</span>);</span><br><span class="line">        SqlSessionFactory sessionFactory = <span class="hljs-keyword">new</span> SqlSessionFactoryBuilder().build(resource);</span><br><span class="line">        SqlSession session = sessionFactory.openSession();</span><br><span class="line">        ArticleMapper mapper = session.getMapper(ArticleMapper.class);</span><br><span class="line">        Article article1 = mapper.getArticleById(<span class="hljs-number">1L</span>);</span><br><span class="line">        Article article2 = mapper.getArticleById(<span class="hljs-number">1L</span>);</span><br><span class="line">        System.out.println(article1 == article2);  <span class="hljs-comment">//输出true</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行结果：</p>


<p>需要注意的是，这是在单独使用MyBatis时进行的以及缓存测试，<code>如果MyBatis与Spring整合，那么MyBatis的一级缓存可能会失效</code>，详情参见 <a href="https://blog.csdn.net/ctwy291314/article/details/81938882" target="_blank" rel="noopener">https://blog.csdn.net/ctwy291314/article/details/81938882</a></p>
<h2 id="二级缓存"><a href="#二级缓存" class="headerlink" title="二级缓存"></a><strong>二级缓存</strong></h2><p>​    MyBatis的二级缓存是Application级别的缓存，它可以提高对数据库查询的效率，以提高应用的性能。</p>


<p>SqlSessionFactory层面上的二级缓存默认是不开启的，二级缓存的开启需要进行配置，<code>实现二级缓存的时候，MyBatis要求返回的POJO必须是可序列化的</code>。 </p>
<h3 id="二级缓存的配置步骤"><a href="#二级缓存的配置步骤" class="headerlink" title="二级缓存的配置步骤"></a><strong>二级缓存的配置步骤</strong></h3><h4 id="第一步：配置SqlMapConfig-xml（可省略）"><a href="#第一步：配置SqlMapConfig-xml（可省略）" class="headerlink" title="第一步：配置SqlMapConfig.xml（可省略）"></a>第一步：配置SqlMapConfig.xml（可省略）</h4><p>我们实质上需要在全局配置文件中开启配置文件中的所有映射器已经配置的任何缓存，也就是<code>cacheEnabled</code>属性，但是这个属性默认值为true，所以实际上我们可以省略该步骤。</p>
<figure class="highlight xml hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">settings</span>&gt;</span></span><br><span class="line">	<span class="hljs-tag">&lt;<span class="hljs-name">setting</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"cacheEnabled"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"true"</span>/&gt;</span></span><br><span class="line"><span class="hljs-tag">&lt;/<span class="hljs-name">settings</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="第二步：配置映射文件"><a href="#第二步：配置映射文件" class="headerlink" title="第二步：配置映射文件"></a>第二步：配置映射文件</h4><h5 id="cache标签配置"><a href="#cache标签配置" class="headerlink" title="cache标签配置"></a>cache标签配置</h5><p>若要要启用全局的二级缓存，只需要在你的 SQL 映射文件中添加一行：</p>
<figure class="highlight xml hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">cache</span>/&gt;</span></span><br></pre></td></tr></table></figure>

<p>这个简单语句的效果如下:</p>
<ul>
<li>映射语句文件中的所有 select 语句的结果将会被缓存。</li>
<li>映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。</li>
<li>缓存会使用最近最少使用算法（LRU, Least Recently Used）算法来清除不需要的缓存。</li>
<li>缓存不会定时进行刷新（也就是说，没有刷新间隔）。</li>
<li>缓存会保存列表或对象（无论查询方法返回哪种）的 1024 个引用。</li>
<li>缓存会被视为读/写缓存，这意味着获取到的对象并不是共享的，可以安全地被调用者修改，而不干扰其他调用者或线程所做的潜在修改。</li>
</ul>
<h5 id="cache标签的属性："><a href="#cache标签的属性：" class="headerlink" title="cache标签的属性："></a><strong>cache标签的属性</strong>：</h5><ul>
<li>eviction：设置缓存的清除策略，默认值为LRU<ul>
<li><code>LRU</code> – 最近最少使用：移除最长时间不被使用的对象。</li>
<li><code>FIFO</code> – 先进先出：按对象进入缓存的顺序来移除它们。</li>
<li><code>SOFT</code> – 软引用：基于垃圾回收器状态和软引用规则移除对象。</li>
<li><code>WEAK</code> – 弱引用：更积极地基于垃圾收集器状态和弱引用规则移除对象。</li>
</ul>
</li>
<li>flushInterval：（刷新间隔）属性可以被设置为任意的正整数，设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置，也就是没有刷新间隔，缓存仅仅会在调用语句时刷新。</li>
<li>size：（引用数目）属性可以被设置为任意正整数，要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。</li>
<li>readOnly：（只读）属性可以被设置为 true 或 false（默认值）。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。<code>而可读写的缓存会（通过反序列化）返回缓存对象的拷贝。 速度上会慢一些，但是更安全，因此默认值是 false</code>。</li>
</ul>
<h5 id="SQL语句标签配置"><a href="#SQL语句标签配置" class="headerlink" title="SQL语句标签配置"></a><strong>SQL语句标签配置</strong></h5><p>​    缓存的配置和缓存实例会被绑定到 SQL 映射文件的命名空间中。 因此，同一命名空间中的所有语句和缓存将通过命名空间绑定在一起。<code>每条语句可以自定义与缓存交互的方式，或将它们完全排除于缓存之外，这可以通过在每条语句上使用两个简单属性来达成</code>。 默认情况下，语句会这样来配置：</p>
<figure class="highlight xml hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">...</span> <span class="hljs-attr">flushCache</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">useCache</span>=<span class="hljs-string">"true"</span>/&gt;</span></span><br><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">insert</span> <span class="hljs-attr">...</span> <span class="hljs-attr">flushCache</span>=<span class="hljs-string">"true"</span>/&gt;</span></span><br><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">update</span> <span class="hljs-attr">...</span> <span class="hljs-attr">flushCache</span>=<span class="hljs-string">"true"</span>/&gt;</span></span><br><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">delete</span> <span class="hljs-attr">...</span> <span class="hljs-attr">flushCache</span>=<span class="hljs-string">"true"</span>/&gt;</span></span><br></pre></td></tr></table></figure>

<p>​    鉴于这是默认行为，显然你永远不应该以这样的方式显式配置一条语句。但如果你想改变默认的行为，只需要设置 flushCache 和 useCache 属性。比如，某些情况下你可能希望特定 select 语句的结果排除于缓存之外，或希望一条 select 语句清空缓存。类似地，你可能希望某些 update 语句执行时不要刷新缓存。</p>




<figure class="highlight xml hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"getArticleById"</span> <span class="hljs-attr">resultMap</span>=<span class="hljs-string">"articleMap"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"long"</span> <span class="hljs-attr">useCache</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">flushCache</span>=<span class="hljs-string">"false"</span>&gt;</span></span><br><span class="line">	select * from article where id = #&#123;id&#125;</span><br><span class="line"><span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="第三步：让实体类实现Serializable接口"><a href="#第三步：让实体类实现Serializable接口" class="headerlink" title="第三步：让实体类实现Serializable接口"></a>第三步：让实体类实现Serializable接口</h4><p>由于<code>&lt;cache/&gt;</code>标签readOnly标签默认是false，所以MyBatis在读写缓存是通过序列化与反序列化完成的。</p>
<h3 id="二级缓存测试"><a href="#二级缓存测试" class="headerlink" title="二级缓存测试"></a><strong>二级缓存测试</strong></h3><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FirstCachedTest</span> </span>&#123;</span><br><span class="line">    <span class="hljs-meta">@Test</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        InputStream resource = Resources.getResourceAsStream(<span class="hljs-string">"SqlMapConfig.xml"</span>);</span><br><span class="line">        SqlSessionFactory sessionFactory = <span class="hljs-keyword">new</span> SqlSessionFactoryBuilder().build(resource);</span><br><span class="line">        SqlSession session = sessionFactory.openSession();</span><br><span class="line">        ArticleMapper mapper = session.getMapper(ArticleMapper.class);</span><br><span class="line">        Article article1 = mapper.getArticleById(<span class="hljs-number">1L</span>);</span><br><span class="line">        session.close();</span><br><span class="line">        <span class="hljs-comment">//开启一个新的SqlSession，在不同的SqlSession中才会读取二级缓存（全局缓存）</span></span><br><span class="line">        session=sessionFactory.openSession();</span><br><span class="line">        ArticleMapper mapper2 = session.getMapper(ArticleMapper.class);</span><br><span class="line">        Article article2 = mapper2.getArticleById(<span class="hljs-number">1L</span>);</span><br><span class="line">        System.out.println(article1 == article2);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行结果：</p>


<h3 id="二级缓存需要注意的地方"><a href="#二级缓存需要注意的地方" class="headerlink" title="二级缓存需要注意的地方"></a><strong>二级缓存需要注意的地方</strong></h3><p>​    对于查询多commit少且用户对查询结果实时性要求不高，此时采用mybatis二级缓存技术降低数据库访问量，提高访问速度。但不能滥用二级缓存，二级缓存也有很多弊端，从MyBatis默认二级缓存是关闭的就可以看出来。二级缓存是建立在同一个namespace下的，如果对表的操作查询可能有多个namespace，那么得到的数据就是错误的。</p>
<p>​    举例来说：文章和标签，ArticleMapper、TagMapper。在查询文章时我们需要把文章对应的标签也查询出来，那么这个标签信息被二级缓存在ArticleMapper对应的namespace下，这个时候有人要修改Tag的基本信息，那就是在TagMapper的namespace下修改，他是不会影响到ArticleMapper的缓存的，那么你再次查找文章数据时，拿到的是缓存的数据，这个数据其实已经是过时的。</p>
<h3 id="二级缓存数据过期问题测试"><a href="#二级缓存数据过期问题测试" class="headerlink" title="二级缓存数据过期问题测试"></a><strong>二级缓存数据过期问题测试</strong></h3><p>Article和Tag是一对多的关系，其中Aticle是一，Tag是多。</p>
<p><strong>ArticleMapper</strong></p>
<figure class="highlight xml hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">mapper</span> <span class="hljs-attr">namespace</span>=<span class="hljs-string">"com.tjd.spring_mybatis_plus.mapper.ArticleMapper"</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">cache</span> /&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">resultMap</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"articleMap"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"Article"</span>&gt;</span></span><br><span class="line">        <span class="hljs-tag">&lt;<span class="hljs-name">id</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"id"</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"id"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span></span><br><span class="line">        <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"title"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">result</span>&gt;</span></span><br><span class="line">        <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"content"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">result</span>&gt;</span></span><br><span class="line">        <span class="hljs-tag">&lt;<span class="hljs-name">collection</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"tags"</span> <span class="hljs-attr">ofType</span>=<span class="hljs-string">"Tag"</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"id"</span> <span class="hljs-attr">select</span>=<span class="hljs-string">"com.tjd.spring_mybatis_plus.mapper.TagMapper.getTagsByArticleId"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">collection</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;/<span class="hljs-name">resultMap</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"getArticleById"</span> <span class="hljs-attr">resultMap</span>=<span class="hljs-string">"articleMap"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"long"</span> <span class="hljs-attr">useCache</span>=<span class="hljs-string">"true"</span>&gt;</span></span><br><span class="line">      select * from article where id = #&#123;id&#125;</span><br><span class="line">    <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span></span><br><span class="line"><span class="hljs-tag">&lt;/<span class="hljs-name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><strong>TagMapper</strong></p>
<figure class="highlight xml hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-tag">&lt;<span class="hljs-name">mapper</span> <span class="hljs-attr">namespace</span>=<span class="hljs-string">"com.tjd.spring_mybatis_plus.mapper.TagMapper"</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">cache</span>/&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">resultMap</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tagMap"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"Tag"</span>&gt;</span></span><br><span class="line">        <span class="hljs-tag">&lt;<span class="hljs-name">id</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"id"</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"id"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span></span><br><span class="line">        <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"content"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">result</span>&gt;</span></span><br><span class="line">        <span class="hljs-tag">&lt;<span class="hljs-name">association</span>  <span class="hljs-attr">property</span>=<span class="hljs-string">"article"</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"article_id"</span> <span class="hljs-attr">javaType</span>=<span class="hljs-string">"Article"</span> <span class="hljs-attr">select</span>=<span class="hljs-string">"com.tjd.spring_mybatis_plus.mapper.ArticleMapper.getArticleById"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">association</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;/<span class="hljs-name">resultMap</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">update</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"updateTag"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"Tag"</span>&gt;</span></span><br><span class="line">        update tag  set content=#&#123;content&#125; where id=#&#123;id&#125;</span><br><span class="line">    <span class="hljs-tag">&lt;/<span class="hljs-name">update</span>&gt;</span></span><br><span class="line">    <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"getTagsByArticleId"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"long"</span> <span class="hljs-attr">resultMap</span>=<span class="hljs-string">"tagMap"</span>&gt;</span></span><br><span class="line">        select * from tag where article_id=#&#123;article_id&#125;</span><br><span class="line">    <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span></span><br><span class="line"><span class="hljs-tag">&lt;/<span class="hljs-name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><strong>测试代码</strong></p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-meta">@RunWith</span>(SpringJUnit4ClassRunner.class)</span><br><span class="line"><span class="hljs-meta">@ContextConfiguration</span>(<span class="hljs-string">"classpath:applicationContext.xml"</span>)</span><br><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecondCachedErrorTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Autowired</span></span><br><span class="line">    <span class="hljs-keyword">private</span> ArticleMapper articleMapper;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Autowired</span></span><br><span class="line">    <span class="hljs-keyword">private</span> TagMapper tagMapper;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-meta">@Test</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="hljs-comment">//第一次查询，查询出来的aticle对象被缓存在ArticleMapper的namespace下</span></span><br><span class="line">        Article article = articleMapper.getArticleById(<span class="hljs-number">1L</span>);</span><br><span class="line">        Tag tag = article.getTags().get(<span class="hljs-number">0</span>);</span><br><span class="line">        tag.setContent(<span class="hljs-string">"dasdas"</span>);</span><br><span class="line">        <span class="hljs-comment">//更新Tag，那么TagMapper下的二级缓存被刷新（清空）</span></span><br><span class="line">        tagMapper.updateTag(tag);</span><br><span class="line">        <span class="hljs-comment">//再次查询Article，此时获得是缓存数据，而关联的tag数据已经过时</span></span><br><span class="line">        Article article2 = articleMapper.getArticleById(<span class="hljs-number">1L</span>);</span><br><span class="line">        <span class="hljs-comment">//TagMapper对应的namespace下的缓存由于在更新时被刷新（清空），所以查询的结果是正确的</span></span><br><span class="line">        List&lt;Tag&gt; tags = tagMapper.getTagsByArticleId(<span class="hljs-number">1L</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>​    <strong>根据以上测试，我们明白想要使用二级缓存时需要想好两个问题</strong>：</p>
<ul>
<li>对该表的操作与查询都在同一个namespace下，其他的namespace如果有操作，就会发生缓存数据过期的问题。</li>
<li>对关联表的查询，关联的所有表的操作都必须在同一个namespace。</li>
<li>在有多表查询的情况下建议不使用二级缓存。</li>
</ul>
<h2 id="两级缓存的优先级"><a href="#两级缓存的优先级" class="headerlink" title="两级缓存的优先级"></a><strong>两级缓存的优先级</strong></h2><p>如果两级缓存同时开启，那么二级缓存比一级缓存优先级高，也就是在执行数据库查询操作时，优先读取二级缓存中的内容。</p>
<p>文章参考至：</p>
<p><a href="https://blog.csdn.net/ctwy291314/article/details/81938882" target="_blank" rel="noopener">https://blog.csdn.net/ctwy291314/article/details/81938882</a></p>
<p><a href="https://www.cnblogs.com/happyflyingpig/p/7739749.html" target="_blank" rel="noopener">https://www.cnblogs.com/happyflyingpig/p/7739749.html</a></p>
<p><a href="https://www.cnblogs.com/yuluoxingkong/p/8205858.html" target="_blank" rel="noopener">https://www.cnblogs.com/yuluoxingkong/p/8205858.html</a></p>
<p><a href="http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html" target="_blank" rel="noopener">http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html</a></p>

        </div>
        
        <div class="level is-size-7 is-uppercase">
            <div class="level-start">
                <div class="level-item">
                    <span class="is-size-6 has-text-grey has-mr-7">#</span>
                    <a class="has-link-grey -link" href="/tags/MyBatis/">MyBatis</a>, <a class="has-link-grey -link" href="/tags/缓存/">缓存</a>
                </div>
            </div>
        </div>
        
        
        
        
<div class="sharethis-inline-share-buttons"></div>
<script type="text/javascript" src="//platform-api.sharethis.com/js/sharethis.js#property=5d1b4ddd55a8b200110cdf62&amp;product=social-ab" async="async"></script>

        
    </div>
</div>



<div class="card">
    <div class="card-content">
        <h3 class="menu-label has-text-centered">喜欢这篇文章？打赏一下作者吧</h3>
        <div class="buttons is-centered">
            
                
<a class="button is-info donate">
    <span class="icon is-small">
        <i class="fab fa-alipay"></i>
    </span>
    <span>支付宝</span>
    <div class="qrcode"><img src="/images/alipay.jpg" alt="支付宝"></div>
</a>

                
                
<a class="button is-success donate">
    <span class="icon is-small">
        <i class="fab fa-weixin"></i>
    </span>
    <span>微信</span>
    <div class="qrcode"><img src="/images/wechat.png" alt="微信"></div>
</a>

                
        </div>
    </div>
</div>



<div class="card card-transparent">
    <div class="level post-navigation is-flex-wrap is-mobile">
        
        <div class="level-end">
            <a class="level level-item has-link-grey  article-nav-next" href="/article/49653.html">
                <i class="level-item fas fa-chevron-left"></i>
                <span class="level-item">Mybatis中各种数据的映射类型</span>
            </a>
        </div>
        
        
        <div class="level-start">
            <a class="level level-item has-link-grey  article-nav-prev" href="/article/34137.html">
                <span class="level-item">事务并发所带来的问题（转）</span>
                <i class="level-item fas fa-chevron-right"></i>
            </a>
        </div>
        
    </div>
</div>



<div class="card">
    <div class="card-content">
        <h3 class="title is-5 has-text-weight-normal">评论</h3>
        
<div id="comment-container"></div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1.4.1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1.4.1/dist/gitalk.min.js"></script>
<script>
    var gitalk = new Gitalk({
        clientID: 'a362d7e659d5da0b4c24',
        clientSecret: 'bfd2817b484ba4d45ad14e44bbf993fa353a4f2d',
        id: 'c630ede0bd60f4f5ff718809af3e729e',
        repo: 'blog_comment',
        owner: 'tianjindong',
        admin: "tianjindong"
    })
    gitalk.render('comment-container')
</script>

    </div>
</div>
</div>
                




<div class="column is-4-tablet is-3-desktop is-3-widescreen  has-order-1 column-left ">
    
        
<div class="card widget">
    <div class="card-content">
        <nav class="level">
            <div class="level-item has-text-centered">
                <div>
                    
                        <img class="image is-128x128 has-mb-6" src="/images/avatar.png" alt="听到微笑">
                    
                    
                    <p class="is-size-4 is-block">
                        听到微笑
                    </p>
                    
                    
                    <p class="is-size-6 is-block">
                        Java Engineer
                    </p>
                    
                    
                    <p class="is-size-6 is-flex is-flex-center has-text-grey">
                        <i class="fas fa-map-marker-alt has-mr-7"></i>
                        <span>中国 上海</span>
                    </p>
                    
                </div>
            </div>
        </nav>
        <nav class="level is-mobile">
            <div class="level-item has-text-centered is-marginless">
                <div>
                    <p class="heading">
                        文章
                    </p>
                    <p class="title has-text-weight-normal">
                        54
                    </p>
                </div>
            </div>
            <div class="level-item has-text-centered is-marginless">
                <div>
                    <p class="heading">
                        分类
                    </p>
                    <p class="title has-text-weight-normal">
                        20
                    </p>
                </div>
            </div>
            <div class="level-item has-text-centered is-marginless">
                <div>
                    <p class="heading">
                        标签
                    </p>
                    <p class="title has-text-weight-normal">
                        30
                    </p>
                </div>
            </div>
        </nav>
        <div class="level">
            <a class="level-item button is-link is-rounded" href="https://github.com/tianjindong" target="_blank">
                关注我</a>
        </div>
        
        
        <div class="level is-mobile">
            
            <a class="level-item button is-white is-marginless" target="_blank" title="Github" href="https://github.com/tianjindong">
                
                <i class="iconfont-github icon-GitHub"></i>
                
            </a>
            
            <a class="level-item button is-white is-marginless" target="_blank" title="Gitee" href="https://gitee.com/tianjindong">
                
                <i class="iconfont-gitee icon-gitee"></i>
                
            </a>
            
            <a class="level-item button is-white is-marginless" target="_blank" title="CSDN" href="https://blog.csdn.net/tianjindong0804">
                
                <i class="iconfont-csdn icon-CN_csdnnet"></i>
                
            </a>
            
            <a class="level-item button is-white is-marginless" target="_blank" title="Twitter" href="https://twitter.com/HearingSmile">
                
                <i class="iconfont-twitter icon-twitter"></i>
                
            </a>
            
        </div>
        
    </div>
</div>
    
        
<div class="card widget" id="toc">
    <div class="card-content">
        <div class="menu">
            <h3 class="menu-label">
                目录
            </h3>
            <ul class="menu-list"><li>
        <a class="is-flex" href="#一级缓存">
        <span class="has-mr-6">1</span>
        <span>一级缓存</span>
        </a><ul class="menu-list"><li>
        <a class="is-flex" href="#一级缓存的生命周期">
        <span class="has-mr-6">1.1</span>
        <span>一级缓存的生命周期</span>
        </a></li><li>
        <a class="is-flex" href="#如何判断两次查询是完全相同的呢">
        <span class="has-mr-6">1.2</span>
        <span>如何判断两次查询是完全相同的呢</span>
        </a></li><li>
        <a class="is-flex" href="#一级缓存的测试">
        <span class="has-mr-6">1.3</span>
        <span>一级缓存的测试</span>
        </a></li></ul></li><li>
        <a class="is-flex" href="#二级缓存">
        <span class="has-mr-6">2</span>
        <span>二级缓存</span>
        </a><ul class="menu-list"><li>
        <a class="is-flex" href="#二级缓存的配置步骤">
        <span class="has-mr-6">2.1</span>
        <span>二级缓存的配置步骤</span>
        </a><ul class="menu-list"><li>
        <a class="is-flex" href="#第一步：配置SqlMapConfig-xml（可省略）">
        <span class="has-mr-6">2.1.1</span>
        <span>第一步：配置SqlMapConfig.xml（可省略）</span>
        </a></li><li>
        <a class="is-flex" href="#第二步：配置映射文件">
        <span class="has-mr-6">2.1.2</span>
        <span>第二步：配置映射文件</span>
        </a></li><li>
        <a class="is-flex" href="#第三步：让实体类实现Serializable接口">
        <span class="has-mr-6">2.1.3</span>
        <span>第三步：让实体类实现Serializable接口</span>
        </a></li></ul></li><li>
        <a class="is-flex" href="#二级缓存测试">
        <span class="has-mr-6">2.2</span>
        <span>二级缓存测试</span>
        </a></li><li>
        <a class="is-flex" href="#二级缓存需要注意的地方">
        <span class="has-mr-6">2.3</span>
        <span>二级缓存需要注意的地方</span>
        </a></li><li>
        <a class="is-flex" href="#二级缓存数据过期问题测试">
        <span class="has-mr-6">2.4</span>
        <span>二级缓存数据过期问题测试</span>
        </a></li></ul></li><li>
        <a class="is-flex" href="#两级缓存的优先级">
        <span class="has-mr-6">3</span>
        <span>两级缓存的优先级</span>
        </a></li></ul>
        </div>
    </div>
</div>

    
        
    
    
        <div class="column-right-shadow  is-sticky">
        
            
<div class="card widget">
    <div class="card-content">
        <h3 class="menu-label">
            最新文章
        </h3>
        
        <article class="media">
            
            <a href="/article/37883.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="http://image.bigcoder.cn/20210116215841.png" alt="DockerSwarm架构详解(转载)">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2021-01-16T13:46:04.000Z">2021-01-16</time></div>
                    <a href="/article/37883.html" class="has-link-black-ter is-size-6">DockerSwarm架构详解(转载)</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/Docker/">Docker</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/54308.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/h4.jpg" alt="B树和B+树与磁盘读写之间的联系">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-24T02:20:31.000Z">2019-08-24</time></div>
                    <a href="/article/54308.html" class="has-link-black-ter is-size-6">B树和B+树与磁盘读写之间的联系</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/21035.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/h3.jpg" alt="什么是B+树">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-19T10:25:05.000Z">2019-08-19</time></div>
                    <a href="/article/21035.html" class="has-link-black-ter is-size-6">什么是B+树</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/8567.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/f6.jpg" alt="什么是B-树">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-19T10:24:50.000Z">2019-08-19</time></div>
                    <a href="/article/8567.html" class="has-link-black-ter is-size-6">什么是B-树</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/62369.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/h1.jpg" alt="什么是一致性Hash算法">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-16T07:27:10.000Z">2019-08-16</time></div>
                    <a href="/article/62369.html" class="has-link-black-ter is-size-6">什么是一致性Hash算法</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
    </div>
</div>

        
            
        
        </div>
    
</div>

                
            </div>
        </div>
    </section>
    <footer class="footer">
    <div class="container">
        <div class="level">
            <div class="level-start has-text-centered-mobile">
                <a class="footer-logo is-block has-mb-6" href="/">
                
                    <img src="/images/logo.png" alt="MyBatis两级缓存机制详解" height="28">
                
                </a>
                <p class="is-size-7">
                &copy; 2021 听到微笑&nbsp;
                Powered by <a href="https://hexo.io/" target="_blank">Hexo</a> & <a href="https://github.com/ppoffice/hexo-theme-icarus" target="_blank">Icarus</a>
                
                </p>
            </div>
            <div class="level-end">
            
            </div>
        </div>
    </div>
</footer>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.22.2/min/moment-with-locales.min.js"></script>
<script>moment.locale("zh-CN");</script>


    
    
    
    <script src="/js/animation.js"></script>
    

    
    
    
    <script src="https://cdn.jsdelivr.net/npm/lightgallery@1.6.8/dist/js/lightgallery.min.js" defer></script>
    <script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js" defer></script>
    <script src="/js/gallery.js" defer></script>
    

    
    

<div id="outdated">
    <h6>Your browser is out-of-date!</h6>
    <p>Update your browser to view this website correctly. <a id="btnUpdateBrowser" href="http://outdatedbrowser.com/">Update
            my browser now </a></p>
    <p class="last"><a href="#" id="btnCloseUpdateBrowser" title="Close">&times;</a></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/outdatedbrowser@1.1.5/outdatedbrowser/outdatedbrowser.min.js" defer></script>
<script>
    document.addEventListener("DOMContentLoaded", function () {
        outdatedBrowser({
            bgColor: '#f25648',
            color: '#ffffff',
            lowerThan: 'flex'
        });
    });
</script>


    
    
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.5/unpacked/MathJax.js?config=TeX-MML-AM_CHTML" defer></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
    MathJax.Hub.Config({
        'HTML-CSS': {
            matchFontHeight: false
        },
        SVG: {
            matchFontHeight: false
        },
        CommonHTML: {
            matchFontHeight: false
        },
        tex2jax: {
            inlineMath: [
                ['$','$'],
                ['\\(','\\)']
            ]
        }
    });
});
</script>

    
    

<a id="back-to-top" title="回到顶端" href="javascript:;">
    <i class="fas fa-chevron-up"></i>
</a>
<script src="/js/back-to-top.js" defer></script>


    
    

    
    
    
    

    
    
    
    
    
    <script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.4/dist/clipboard.min.js" defer></script>
    <script src="/js/clipboard.js" defer></script>
    

    
    
    


<script src="/js/main.js" defer></script>

    
    <div class="searchbox ins-search">
    <div class="searchbox-container ins-search-container">
        <div class="searchbox-input-wrapper">
            <input type="text" class="searchbox-input ins-search-input" placeholder="想要查找什么...">
            <span class="searchbox-close ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="searchbox-result-wrapper ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
    (function (window) {
        var INSIGHT_CONFIG = {
            TRANSLATION: {
                POSTS: '文章',
                PAGES: '页面',
                CATEGORIES: '分类',
                TAGS: '标签',
                UNTITLED: '(无标题)',
            },
            CONTENT_URL: '/content.json',
        };
        window.INSIGHT_CONFIG = INSIGHT_CONFIG;
    })(window);
</script>
<script src="/js/insight.js" defer></script>
<link rel="stylesheet" href="/css/search.css">
<link rel="stylesheet" href="/css/insight.css">
    
</body>
</html>